#pragma once
#include <time.h>
#include <string>
#include <boost/thread/thread_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "GLog.h"

using namespace std;
using namespace boost;
using namespace boost::posix_time;

namespace hoyosvr
{

	class Time;

	// return single time instance
	extern Time * GetScheduler();

	namespace time_helper
	{
		string					print_time_string(time_t t,bool is_print = false);
		unsigned				get_current_time(void);
		unsigned				calc_time(time_t start_time,int hour,int min,int sec);
		bool					get_duration_time(time_t time_start, time_t time_end, int& hour, int& min, int& sec);

		tm						localTime(time_t now);
		tm						localTime(void);
		unsigned				nextDay(int offset = 0, unsigned now = 0);
		unsigned				nextHalfHour(unsigned now = 0);
		unsigned				nextHour(unsigned now = 0);
		unsigned				nextWeek(int offset = 0, unsigned now = 0);
		unsigned				next2Minute(unsigned now = 0);

		void					showTime(unsigned t);
		void					showTime(tm t);

		int						timeZone(void);

		//milisecond 
		void					sleep(int mili_secs);
		void					sleepMicroSec(int microSec );
	}

	///////////////////////////////////////////////////////////////////////////////
	// ScheduledCallbackProc
	///////////////////////////////////////////////////////////////////////////////
	class ScheduledListener
	{

	public:

		virtual ~ScheduledListener() {}

		virtual void run( int param ) = 0;
	};

	// Forward References /////////////////////////////////////////////////////////
	// Constants //////////////////////////////////////////////////////////////////
	const char WeekDayNames[][16] =
	{
		"Sunday",
		"Monday",
		"Tuesday",
		"Wednesday",
		"Thursday",
		"Friday",
		"Saturday",
	};

	const char MonthNames[][16] =
	{
		"January",
		"Febrary",
		"March",
		"April",
		"May",
		"June",
		"July",
		"August",
		"September",
		"October",
		"November",
		"December",
	};

	class Time
	{
	public:
		enum ScheduledType
		{
			SCHEDULED_TYPE_IMMEDIATELY = 0,
			SCHEDULED_TYPE_ANY_PERIOD,
			SCHEDULED_TYPE_FIXED_SEC,
			SCHEDULED_TYPE_FIXED_MIN,
			SCHEDULED_TYPE_FIXED_HOUR,
			SCHEDULED_TYPE_FIXED_DAY,
			SCHEDULED_TYPE_FIXED_WEEKDAY,
			SCHEDULED_TYPE_FIXED_CHECKED,
			SCHEDULED_TYPE_APPOINT_TIME,
		};

	public:

		Time( bool updateTime = false );
		virtual ~Time();
		static Time*					instance();

		// Accessor
		tm*								getTM() { return &m_tm; }
		tm*								getTM2() { return &m_tm2; }

		uint64_t						update();
		void							updateDateTimeNow();
		void							schedule();
		void							time_loop();

		// Timer
		//void							setDays( long days );
		//void							setHours( long hours );
		//void							setMins( long mins );
		//void							setSecs( long secs );
		//void							setTicks( long ticks );
		//long							getDays() const;
		//long							getHours() const;
		//long							getMins() const;
		//long							getSecs() const;
		//long							getTicks() const;

		string							updateDateTimeAddHour( int hour );
		string							updateDateTimeAddSecond( int sec );
		string							get_time_from_input_str( const char* time_para, bool imei = false );
		string							get_base_time_from_input_str( const char* time_para );
		uint64_t						get_diff_time_from_input_str( uint64_t time_para );
		uint64_t						get_cur_system_second();
		uint64_t						get_cur_time_milli_seconds();

		string							get_date_string_by_time_interval( const char* time = NULL, int sec = 0, int type = 3 );
		time_t							get_second_by_date_string( const char* time );

		//faction
		bool	                        get_time_pass_days( tm * time ,const char* dest_time );
		bool							compare_time_less_than_current_time( const char* time,uint32_t type );
		bool							time_pass( const char* time, int sec );
		string							calc_time_by_base_time( const char* time, int sec );
		// Operators
		bool							operator > ( const Time& time ) const;
		bool							operator < ( const Time& time ) const;
		bool							operator == ( const Time& time ) const;
		bool							operator >= ( const Time& time ) const;
		bool							operator <= ( const Time& time ) const;
		bool							operator != ( const Time& time ) const;

		// just commented at the current version
		// if needed, please re-implement the below with ptime again
		//Time							operator + ( const Time& time ) const;
		time_duration					operator - ( const Time& time ) const;
		//Time							operator += ( const Time& time );
		//Time							operator -= ( const Time& time );

		// Date
		inline int						getDateYear() const;
		inline int						getDateMonth() const;
		inline int						getDateDay() const;
		inline int						getDateWeek() const;							    
		inline int						getDateHour() const;
		inline int						getDateMinute() const;
		inline int						getDateSecond() const;
		inline int						getDateMinute2() const;
		inline int						getDateSecond2() const;
		inline void						updateDateFormat();
		inline const char*				getDateFormat() const;
		inline const char*				getDateTimeNow() const;

		// Scheduled event
		void							registerScheduledEvent( ScheduledListener* proc, void* expiration, bool removedOnExpiration, int type = SCHEDULED_TYPE_ANY_PERIOD, int param = 0, const char* func = NULL );
		void							modifyScheduleExpiration( ScheduledListener* proc, void* expiration, int para );
		void							removeScheduledEvent( ScheduledListener* proc, void* expiration, int param = 0 );

	private:
		struct ScheduledEvent
		{
			ScheduledListener*			m_proc;
			void*						m_expiration;
			int							m_type;
			int							m_param;
			bool						m_expired;
			bool						m_remove;
			string						_func_str;
		};

		struct ScheduledEventHanlder
		{
			Time		*m_time;

			ScheduledEventHanlder( Time* time ) { m_time = time; }

			void operator()( ScheduledEvent& e );
		};

		struct ScheduledEventEraser
		{
			bool operator()( ScheduledEvent& e )
			{
				return e.m_expired && e.m_remove;
			}
		};

	private:
		ptime							m_time;
		tm								m_tm;
		tm								m_tm2;
		string							m_dateFormat;
		string							m_datetimeNow;
		string							m_datetimeAdd;
		time_t							callback_sec;
		// Event
		std::vector< ScheduledEvent >	m_events;

		static	Time*					instance_p;

	};


	// Inline Methods /////////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateYear
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateYear() const
	{
		return m_tm.tm_year + 1900;
	}
	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateMonth
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateMonth() const
	{
		return m_tm.tm_mon;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateDay
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateDay() const
	{
		return m_tm.tm_mday;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateWeek
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateWeek() const
	{
		return m_tm.tm_wday;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateHour
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateHour() const
	{
		return m_tm.tm_hour;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateMinutes
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateMinute() const
	{
		return m_tm.tm_min;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateSeconds
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateSecond() const
	{
		return m_tm.tm_sec;
	}

	inline int Time::getDateMinute2() const
	{
		return m_tm2.tm_min;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateSeconds
	///////////////////////////////////////////////////////////////////////////////
	inline int Time::getDateSecond2() const
	{
		return m_tm2.tm_sec;
	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::updateDateFormat
	///////////////////////////////////////////////////////////////////////////////
	inline void Time::updateDateFormat()
	{
		ostringstream oss;
		ptime curTime = ptime_from_tm(m_tm);
		time_facet *facet = new time_facet("%Y-%m-%d %H:%M:%S");		
		oss.imbue(locale(cout.getloc(), facet));		
		oss << curTime;
		ostringstream oss2;
		oss2 <<	WeekDayNames[getDateWeek()] << " " << oss.str().c_str();
		m_dateFormat = oss2.str();

	}

	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateFormat
	///////////////////////////////////////////////////////////////////////////////
	inline const char* Time::getDateFormat() const
	{
		return m_dateFormat.c_str();
	}
	///////////////////////////////////////////////////////////////////////////////
	// Time::getDateTimeNow
	///////////////////////////////////////////////////////////////////////////////
	inline const char* Time::getDateTimeNow() const
	{
		return m_datetimeNow.c_str();
	}

}

class time_logger
{
	boost::system_time start_time;
	boost::system_time end_time;
	std::string str;
public:
	time_logger(const char* s)
	{
		start_time = boost::get_system_time();
		str = s;
	}
	~time_logger()
	{
		end_time = boost::get_system_time();
		LogD << "process message " << str << "\t consume : " << (float)((end_time - start_time).total_nanoseconds()/1000000) << " ms" << LogEnd;
	}
};
